Skip to content

Conversation

CMCDragonkai
Copy link
Member

@CMCDragonkai CMCDragonkai commented Sep 16, 2025

Description

This starts the first protocol proposal with PSP-1. PSP stands for "Polykey Standards Proposal".

The first PSP is about Capability Model and Grammar.

Tasks

  • 1. Writing out the first iteration of PSP-1
  • 2. Change bind and caveats towards Program-first model
  • 3. Publish it, and properly deal with ascii and unicode symbols with latex

Final checklist

  • Domain specific tests
  • Full tests
  • Updated inline-comment documentation
  • Lint fixed
  • Squash and rebased
  • Sanity check the final build

@CMCDragonkai
Copy link
Member Author

Executive Decision for PSP‑1 v0.1 (after OT/acceptance review)

Decision: Keep PSP‑1 a compact authorization grammar. Incorporate only minimal hooks needed by OT wedges; push operational security and acceptance complexity to TAP/RAM/PSP‑4. No expansion of CPL/0 beyond the checks‑only kernel.

What we lock for PSP‑1 v0.1

  • Language

    • CPL/0 remains checks‑only, ground terms, pure builtins. No user atoms, variables, rules, recursion, or caveats.
    • PairSet is the only declaration kind in core. ActionSet/ResourceSet can be a PSP‑4 profile later.
  • Pinning

    • Grants MUST pin lang_version and builtins_id; channel_lattice_id only if channel_geq is used. Resource schemes by name. Verbs as strings.
  • Registries

    • Builtins/Channel Lattices/Schemes live in PSP‑4. Unknowns fail closed.
  • Program identity

    • PCF normalization (sort/dedup) + PSP‑3 canonical bytes + program_id. Mismatch ⇒ fail.
  • Biscuit alignment

    • Add an informative note: CPL/0 is the checks‑only fragment mapping 1:1 to Biscuit checks; rules/derivations are out of core and may be added as a TAP‑gated profile later (off‑path AOT or compiled guards).

Minimal additions to cover OT‑driven needs (no new lattices, no core state)

  • Environment facts (optional, TAP‑gated)

    • enforcer_assurance: Str (e.g., “L2‑E187”).
    • enforcer_jurisdiction: Str (e.g., “TW”, “EU‑NIS2”).
    • current_use_count: Int (read‑only projection if TAP requires; informative only).
  • Expression pattern (use existing primitives)

    • Use ctx_eq to assert assurance/jurisdiction claims: ctx_eq("assurance","L2‑E187"), ctx_eq("jurisdiction","TW").
    • Enforcer targeting via ctx_eq("enforcer_did","did:…") or presenter_is where applicable.
    • Integration hygiene/KYB proofs are verified off‑path; CEP injects non‑PII digests/flags in ctx; Programs assert via ctx_eq.
  • Explicitly defer from core

    • No assurance lattice (assurance_geq) in v0.1; if needed, define via PSP‑4 profile later.
    • No maxUses/stateful counters in CPL/0; use CEP/TAP policy (rate‑limit/deny). current_use_count exposure is optional and non‑normative.

Scope boundaries (to avoid creep)

  • PSP‑1 stays an authorization construct:
    • Who/what/when/where/conditions expressed as CPL/0 checks over env facts + finite declarations.
  • TAP/RAM own operational security and acceptance:
    • E187/E188 alignment, time‑source tolerances, SIEM/SOC formats, incident coordination, KYB verification policy, payout rules, regulatory reporting, insurance hooks.
  • PSP‑2 owns receipts; PSP‑3 owns bytes/envelopes.

Edits to apply (succinct)

  • Registries: keep as finalized (pin lang_version, builtins_id, channel_lattice_id when used; fail‑closed rules).
  • Data Model
    • 6.2.2 Evaluation Environment: add optional enforcer_assurance, enforcer_jurisdiction, current_use_count (TAP‑gated).
    • 6.2.3 Builtins and Registries: state that assurance/jurisdiction constraints SHOULD be expressed via ctx_eq; new lattices can be PSP‑4 profiles; stateful policies are TAP/CEP concerns.
    • 6.2.5 Relationship to Biscuit: include the checks‑only mapping note.
  • Optional (Overview): one informative sentence acknowledging the “Settlement/Acceptance” layer (PSP‑2/TAP/RAM) as the cross‑boundary interface; PSP‑1 feeds it.

Next steps

  • Lock Builtins v1 in PSP‑4 (in_pairset, within_time, ttl_ok, channel_geq, ctx_eq, presenter_is).
  • Produce two test vectors (Program → PCF bytes → program_id) and one self‑contained Presentation chain.
  • Add a TAP‑OT profile draft (separate doc) for E187/E188 alignment, time sync tolerances, and assurance labels, using ctx claims and receipts—not new PSP‑1 constructs.

This preserves PSP‑1’s clean, portable authorization kernel while giving OT wedges the minimal hooks they need.

@CMCDragonkai
Copy link
Member Author

I had to create a script to help convert unicode. This script works on the happy path, but there is still bugs on the non-happy paths.

#!/usr/bin/env bash

# asciify.sh — bash-native Unicode → keyboard ASCII normalizer (prose-wide LaTeX for math)
# deps: bash (4+), sed, iconv, mktemp
# usage:
#   asciify.sh < file.txt
#   asciify.sh --in-place file1.md file2.txt
#   asciify.sh --allow '→←≤≥≠°×÷±' < in.txt > out.txt
#   asciify.sh --ellipsis '..' --emdash '-' --endash '-' < in > out
#
set -euo pipefail

# Defaults
ALLOW_CHARS=""
ELLIPSIS="..."   # or '..'
EMDASH="-"       # or '--'
ENDASH="-"       # or '--'
IN_PLACE=0
BACKUP_SUFFIX=".bak"

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --allow)        ALLOW_CHARS="${2:-}"; shift 2 ;;
    --ellipsis)     ELLIPSIS="${2:-...}"; shift 2 ;;
    --emdash)       EMDASH="${2:-"-"}"; shift 2 ;;
    --endash)       ENDASH="${2:-"-"}"; shift 2 ;;
    --in-place)     IN_PLACE=1; shift ;;
    --suffix)       BACKUP_SUFFIX="${2:-}"; shift 2 ;;
    --help|-h)
      cat <<'HLP'
asciify.sh — normalize to keyboard-only ASCII (and map Unicode math to $LaTeX$)

Policy:
  - Convert math-like Unicode to LaTeX in prose: ⊂ ⊆ ∈ ∉ ∪ ∩ ∅ ∖ ≡ ≅ ≈ ≃ ∝ ¬ ∧ ∨ ∀ ∃ × ÷ ± ∑ ∏ √ ∞ · ∘ → $...$
  - Keep basics in ASCII: ≤ → <=, ≥ → >=, ≠ → !=, arrows → ASCII

Options:
  --allow 'CHARS'     Keep these Unicode chars through transliteration (default: none)
  --ellipsis '..|...' Choose ellipsis replacement (default: ...)
  --emdash '-'|'--'   Choose em dash replacement (default: -)
  --endash '-'|'--'   Choose en dash replacement (default: -)
  --in-place          Edit files in place (use --suffix for backups)
  --suffix '.bak'     Backup suffix when using --in-place (default: .bak)

Examples:
  echo 'A ⊆ B and x ∈ A' | asciify.sh
  asciify.sh --allow '°' --in-place notes.md
HLP
      exit 0 ;;
    *) break ;;
  esac
done

# Build a sed script with precise mappings.
# Order matters: do LaTeX math BEFORE generic ASCII fallbacks.
mapfile -t SED_RULES < <(cat <<'SEDMAP'
# --- Unicode math → LaTeX (wrapped) ---
# Sets
s/\xE2\x8A\x82/\$\\subset\$/g;        # ⊂ → $\subset$
s/\xE2\x8A\x86/\$\\subseteq\$/g;      # ⊆ → $\subseteq$
s/\xE2\x8A\x8A/\$\\subsetneq\$/g;     # ⊊ → $\subsetneq$
s/\xE2\x8A\x83/\$\\supset\$/g;        # ⊃ → $\supset$
s/\xE2\x8A\x87/\$\\supseteq\$/g;      # ⊇ → $\supseteq$
s/\xE2\x88\x88/\$\\in\$/g;            # ∈ → $\in$
s/\xE2\x88\x89/\$\\notin\$/g;         # ∉ → $\notin$
s/\xE2\x88\xAA/\$\\cup\$/g;           # ∪ → $\cup$
s/\xE2\x88\xA9/\$\\cap\$/g;           # ∩ → $\cap$
s/\xE2\x88\x85/\$\\emptyset\$/g;      # ∅ → $\emptyset$
s/\xE2\x88\x96/\$\\setminus\$/g;      # ∖ → $\setminus$
# Relations (non-basic)
s/\xE2\x89\xA1/\$\\equiv\$/g;         # ≡ → $\equiv$
s/\xE2\x89\x85/\$\\cong\$/g;          # ≅ → $\cong$
s/\xE2\x89\x88/\$\\approx\$/g;        # ≈ → $\approx$
s/\xE2\x89\x83/\$\\simeq\$/g;         # ≃ → $\simeq$
s/\xE2\x88\x9D/\$\\propto\$/g;        # ∝ → $\propto$
# Logic / quantifiers
s/\xC2\xAC/\$\\neg\$/g;               # ¬ → $\neg$
s/\xE2\x88\xA7/\$\\land\$/g;          # ∧ → $\land$
s/\xE2\x88\xA8/\$\\lor\$/g;           # ∨ → $\lor$
s/\xE2\x88\x80/\$\\forall\$/g;        # ∀ → $\forall$
s/\xE2\x88\x83/\$\\exists\$/g;        # ∃ → $\exists$
# Arithmetic / misc
s/\xC3\x97/\$\\times\$/g;             # × → $\times$
s/\xC3\xB7/\$\\div\$/g;               # ÷ → $\div$
s/\xC2\xB1/\$\\pm\$/g;                # ± → $\pm$
s/\xE2\x88\x91/\$\\sum\$/g;           # ∑ → $\sum$
s/\xE2\x88\x8F/\$\\prod\$/g;          # ∏ → $\prod$
s/\xE2\x88\x9A/\$\\sqrt{}\$/g;        # √ → $\sqrt{}$
s/\xE2\x88\x9E/\$\\infty\$/g;         # ∞ → $\infty$
s/\xC2\xB7/\$\\cdot\$/g;              # · → $\cdot$
s/\xE2\x88\x98/\$\\circ\$/g;          # ∘ → $\circ$

# --- Keep basics in ASCII ---
s/\xE2\x89\xA4/<=/g;          # ≤
s/\xE2\x89\xA5/>=/g;          # ≥
s/\xE2\x89\xA0/!=/g;          # ≠

# --- Quotes & dashes & ellipsis ---
s/\xE2\x80\x98/'/g;          # ‘
s/\xE2\x80\x99/'/g;          # ’
s/\xE2\x80\x9A/'/g;          # ‚
s/\xE2\x80\x9B/'/g;          # ‛
s/\xCA\xB9/'/g;              # ʹ
s/\xE2\x80\x9C/"/g;          # “
s/\xE2\x80\x9D/"/g;          # ”
s/\xE2\x80\x9E/"/g;          # „
s/\xE2\x80\x9F/"/g;          # ‟
s/\xC2\xAB/"/g;              # «
s/\xC2\xBB/"/g;              # »
s/\xE2\x80\xB9/'/g;          # ‹
s/\xE2\x80\xBA/'/g;          # ›
# dashes & hyphens (placeholders; set below)
s/\xE2\x80\x93/@EN_DASH@/g;  # –
s/\xE2\x80\x94/@EM_DASH@/g;  # —
s/\xE2\x80\x95/@EM_DASH@/g;  # ―
s/\xE2\x88\x92/-/g;          # −
s/\xE2\x80\xA6/@ELLIPSIS@/g; # …

# --- Spaces / zero-width ---
s/\xC2\xA0/ /g;              # NBSP
s/\xE2\x80[\x80-\x8A]/ /g;   # en/em/thin/hair spaces
s/\xE2\x80\xAF/ /g; s/\xE2\x81\x9F/ /g; s/\xE3\x80\x80/ /g;
s/\xE2\x80\x8B//g; s/\xE2\x80\x8C//g; s/\xE2\x80\x8D//g; s/\xE2\x81\xA0//g;

# --- Bullets / primes ---
s/\xE2\x80\xA2/*/g; s/\xE2\x97\xA6/*/g; s/\xE2\x80\xA3/*/g; s/\xE2\x96\xAA/*/g;
s/\xE2\x80\xB2/'/g; s/\xE2\x80\xB3/""/g; s/\xE2\x80\xB4/'''/g;

# --- Arrows to ASCII ---
s/\xE2\x86\x92/->/g; s/\xE2\x86\x90/<-/g; s/\xE2\x86\x94/<->/g;
s/\xE2\x87\x92/=>/g; s/\xE2\x87\x90/<=/g; s/\xE2\x87\x94/<=>/g;

# --- Fractions (ASCII) ---
s/\xC2\xBD/1\/2/g; s/\xC2\xBC/1\/4/g; s/\xC2\xBE/3\/4/g;
s/\xE2\x85\x93/1\/3/g; s/\xE2\x85\x94/2\/3/g; s/\xE2\x85\x95/1\/5/g; s/\xE2\x85\x96/2\/5/g; s/\xE2\x85\x97/3\/5/g; s/\xE2\x85\x98/4\/5/g;
s/\xE2\x85\x99/1\/6/g; s/\xE2\x85\x9A/5\/6/g; s/\xE2\x85\x9B/1\/8/g; s/\xE2\x85\x9C/3\/8/g; s/\xE2\x85\x9D/5\/8/g; s/\xE2\x85\x9E/7\/8/g;

# --- Trademarks ---
s/\xC2\xA9/(c)/g; s/\xC2\xAE/(r)/g; s/\xE2\x84\xA2/(tm)/g;
SEDMAP
)

# Prepare sed program string
join_sed() {
  local IFS=$'\n'; printf "%s" "${SED_RULES[*]}"
}
SED_PROG="$(join_sed)"

# Escape replacement
escape_sed_repl() {
  local s="$1"
  s="${s//\\/\\\\}"   # \
  s="${s//&/\\&}"     # &
  s="${s//\//\\/}"    # /
  printf '%s' "$s"
}

# Inject user-chosen dash/ellipsis values
SED_PROG="${SED_PROG//@ELLIPSIS@/$(escape_sed_repl "$ELLIPSIS")}"
SED_PROG="${SED_PROG//@EM_DASH@/$(escape_sed_repl "$EMDASH")}"
SED_PROG="${SED_PROG//@EN_DASH@/$(escape_sed_repl "$ENDASH")}"

# Build allowlist placeholders to protect chosen Unicode through iconv
ALLOW_PLACEHOLDERS=()
if [[ -n "$ALLOW_CHARS" ]]; then
  i=0
  while IFS= read -r -n1 ch; do
    [[ -z "$ch" ]] && continue
    ph="__ALLOW_${i}__"
    SED_PROG+="
s/$(escape_sed_repl "$ch")/$(escape_sed_repl "$ph")/g;
"
    ALLOW_PLACEHOLDERS+=("$ph=$ch")
    ((i++))
  done <<<"$ALLOW_CHARS"
fi

restore_placeholders() {
  local s="$1"
  for kv in "${ALLOW_PLACEHOLDERS[@]}"; do
    local ph="${kv%%=*}"
    local ch="${kv#*=}"
    s="${s//"$ph"/$ch}"
  done
  printf '%s' "$s"
}

process() {
  # 1) targeted sed replacements
  local stage1
  stage1="$(LC_ALL=C sed -Ee "$SED_PROG")"

  # 2) transliterate remaining with iconv (UTF-8 → ASCII), preserving $ and backslashes
  local stage2
  stage2="$(printf '%s' "$stage1" | iconv -f UTF-8 -t ASCII//TRANSLIT//IGNORE -c)"

  # 3) restore any allowlisted characters
  if [[ ${#ALLOW_PLACEHOLDERS[@]} -gt 0 ]]; then
    restore_placeholders "$stage2"
  else
    printf '%s' "$stage2"
  fi
}

# I/O paths (stdin by default; in-place edits if requested)
if [[ $IN_PLACE -eq 1 ]]; then
  if [[ $# -lt 1 ]]; then
    echo "asciify.sh: --in-place requires file paths" >&2; exit 2
  fi
  for f in "$@"; do
    [[ -f "$f" ]] || { echo "skip non-file: $f" >&2; continue; }
    [[ -n "$BACKUP_SUFFIX" ]] && cp -f -- "$f" "$f$BACKUP_SUFFIX" || true
    tmp="$(mktemp)"
    LC_ALL=C process < "$f" > "$tmp"
    mv -- "$tmp" "$f"
  done
else
  LC_ALL=C process
fi

@CMCDragonkai
Copy link
Member Author

GPT-5 Pro review on this.

V = 4

Below is a failure‑seeking review of PSP‑1 with concrete edits. I’ve grouped feedback into: (A) critical inconsistencies and correctness bugs, (B) structural/style changes to make this feel like an RFC/EIP/BIP‑grade spec, (C) oversights and attack surfaces you haven’t addressed, and (D) a clean, regularized ToC plus redlines you can drop in.

I cite relevant public standards where it helps readers triangulate (RFC2119/8174 language, TLS exporters, DPoP, CBOR/COSE, JCS, DSSE, CIDs, Biscuit). I also tie back to your internal docs (Receipt Rails, TAP/RAM, CEP/BA placement) where PSP‑1 leans on those concepts. ([RFC Editor]1) Internals: receipts are enforcer‑minted (PoAR), acceptance is TAP/RAM, BA/CEP vocabulary and placement, etc.


A) Critical inconsistencies & correctness bugs (will break interop)

  1. Atoms, Symbols, and Variables appear, then are forbidden.

    • Where: Terminology says Literals are “Atoms or pure, bounded Builtins” and Terms include Sym/…/Var; the Language Model later says “builtin literals only; … No variables. No user‑defined atoms.” PCF sorting mentions “Builtin before Atom if Atoms are present.”
    • Why this fails: You can’t both ban and accommodate Atoms/Vars. Canonicalization rules also rely on a literal kind that shouldn’t exist.
    • Edit: Delete Atom/Sym/Var from PSP‑1. Say “CPL/0 literals are Builtins only; Terms ∈ {Str, Int, Bool, Bytes}.” Remove the PCF bullet mentioning Atom. Update Appendix A grammar accordingly (see Section D).
  2. Interval boundaries are inconsistent.

    • Where: Algorithm uses now >= iat AND now < exp for Presentation lifetime. Builtin within_time(now, nbf, exp) says true iff nbf <= now <= exp. ttl_ok uses now <= iat + ttl_max.
    • Why this fails: Inclusive vs half‑open windows will produce different “pass/fail” near edges.
    • Edit: Pick one convention across the spec. Recommendation: half‑open windows [nbf, exp), which is what many IETF/JOSE flows use for expiries. State this once in “Time Model & Boundaries.” Align within_time and default Presentation/Grant window checks with half‑open. Cite RFC 7519’s common usage and BCP‑style guidance. ([RFC Editor]2)
  3. Ambiguity in attenuation w.r.t. Checks and Queries.

    • Where: You say “child MAY add Checks; MUST NOT remove any parent Check that controls authority”, and “For any retained parent Query, child Literals ⊇ parent Literals; child MAY add Literals.” You also imply a child may drop Queries (since OR‑removal narrows).

    • Why this fails: “controls authority” is subjective and unverifiable; implementers can’t agree on what counts as “controlling.”

    • Edit: Make the rule syntactic and total:

      • Checks: child MUST include at least the parent’s full set of Checks (by identity after PCF). It MAY add Checks.
      • Queries (inside each Check): child MAY drop any subset of parent Queries (narrowing the OR), or keep and tighten them. For any retained Query, the child’s literal multiset MUST be a superset (constants tightened per builtin rules).
      • Literal constants: only tighten as defined in the Builtins registry.
        This exactly matches the “checks‑only attenuation” pattern in Biscuit (append checks; never remove constraints), which readers will recognize. ([Eclipse Biscuit]3)
  4. TTL semantics are underspecified vs. exp.

    • Where: You say exp - iat SHOULD be short and separately use ttl_ok(iat, now, ttl_max).
    • Why this fails: An implementation could pass ttl_ok while now >= exp denies, or vice‑versa.
    • Edit: Define precedence: the effective Presentation lifetime is ([iat, exp) ∩ ttl_ok). Specify that both must pass; if exp is present, exp - iat SHOULD also be ≤ ttl_max (if both are used), else deny. Tie explicitly to the single captured now. See RSA of JWT exp/iat and JWT BCP for edge guidance. ([RFC Editor]2)
  5. Comparator drift risk across time without pinning.

    • Where: You pin builtins and (if used) the channel lattice, but you do not pin scheme comparator versions; you only select by scheme name.
    • Why this fails: If a scheme’s normalization or subset comparator evolves, two CEPs may render different decisions for the same Grant over time. “Deny on unknown” doesn’t help if semantics change silently.
    • Edit: Either (a) pin a schemes_snapshot_id (a content‑addressed manifest enumerating each scheme name → comparator_id), or (b) require a PoAR to record the comparator fingerprint(s) used at enforcement for audit. I recommend (a) for strict determinism. Use CID/multihash for the manifest. ([GitHub]4)
  6. Channel binding profile naming not tied to IETF registries.

    • Where: You reference TLS exporter and DPoP but don’t name the binding exactly or cite specs.

    • Why this fails: Interop will break if “tls_exporter:v1” isn’t clearly defined relative to TLS 1.3’s exporter binding and its registry.

    • Edit: Define the profile strings and cite the standards:

      • tls-exporter per TLS 1.3 + RFC 9266 (“tls‑exporter” channel binding for TLS 1.3), derived from RFC 5705/8446.
      • dpop per RFC 9449.
        Require channel_lattice_id to enumerate these profile identifiers unambiguously. ([RFC Editor]5)
  7. PCF mentions encodings and kinds you forbid elsewhere.

    • Where: PCF has rules about “indefinite‑length encodings” and “Atoms.”
    • Edit: Keep PCF language entirely format‑agnostic; put all on‑wire determinism in PSP‑3 (CBOR Canonical/Deterministic per RFC 8949 + COSE 9052 or JCS 8785 for JSON‑style encodings; DSSE if you want envelope neutrality). Only define the abstract sort/ordering in PSP‑1. ([IETF Datatracker]6)
  8. Environment fact normalization is partially specified.

    • Where: You require resources to be normalized via scheme comparators, and strings to be NFC, but you don’t state when normalization occurs for env facts.
    • Edit: Require CEPs to normalize input facts—especially resourcebefore evaluation, using the same comparator semantics as declaration canonicalization, or deny. (You already hint at this; promote it to a single, normative rule in “Evaluation Environment”.)
  9. Grant/Presentation cross‑references to TAP/RAM/Receipts are scattered and sometimes conflicting.

    • Where: PSP‑1 claims receipts and acceptance live elsewhere (PSP‑2/TAP), while examples and the algorithm dictate PoAR behavior and denial reason codes.
    • Edit: Move all receipt‑field specifics and reason‑code registries to PSP‑2 and reference them normatively from PSP‑1. The “enforcer mints the proof” invariant and receipt placement (P/R/S) should remain in PSP‑1 as a short invariant and a pointer (your Receipt Rails docs align with this).
  10. Terminology collision around CEP/BA roles and acceptance.

  • Where: PSP‑1 mentions “CEP/BA spec,” anchoring, acceptance/TAP. Your architecture docs already standardize CEP(R)/CEP(P)/CEP(S), BA subtypes, exposure modes, and acceptance responsibilities.
  • Edit: Add a normative terminology subsection with that vocabulary and a stable reference to the CEP/BA placement profile: CEP(R), CEP(P), CEP(S)/SSA; BA is a CEP subtype that bridges non‑native SoA via long‑lived leases; exposure modes mediate/derive/reveal with TAP policy defaults.

B) Structural & style changes (clean, RFC/EIP/BIP‑grade)

B1. “Requirements Language” paragraph
Keep the BCP14 boilerplate exactly per RFC 8174 and 2119 (you already have it, good). Ensure it appears early and applies by default; avoid sprinkling “(normative)” all over. ([RFC Editor]7)

B2. Normalize section headings across all ##
Adopt an RFC/EIP‑like rhythm so each major section is familiar to implementers and reviewers:

1.  Abstract
2.  Terminology and Requirements Language
3.  Overview (goals/non-goals, threat model pointers)
4.  Capability Program (CPL/0)
    4.1 Language Model (structure, monotonicity)
    4.2 Types and Terms
    4.3 Builtins (registry-pinned)
    4.4 Evaluation Environment (facts, normalization)
    4.5 Declarations (PairSet, ActionSet, ResourceSet)
    4.6 Attenuation Semantics
5.  Semantic Pinning
6.  Program Canonical Form (PCF)
7.  Grants
8.  Presentations
9.  Delegation and Attenuation (chain verification)
10. Revocation and Rotation
11. CEP Verification Algorithm (stepwise)
12. Security Considerations
13. IANA / Registry Considerations (if you’ll register op_ids)
14. Test Vectors and Interop
15. References
Appendix A: CPL/0 Grammar (structure-only)
Appendix B: Builtins and Tightening Rules
Appendix C: Scheme Comparator Requirements

B3. Move all on‑wire concerns to PSP‑3.
In PSP‑1, keep PCF purely abstract. In PSP‑3, point to deterministic encodings (CBOR canonical form per RFC 8949 + COSE 9052, or JSON + JCS RFC 8785 + JWS 7515/DSSE). State a single rule: identity = multihash(CanonicalBytes) with algorithm parameters pinned. ([IETF Datatracker]6)

B4. Reference EIP/BIP editorial expectations.
Include short “Rationale”, “Security Considerations”, and “Test Vectors” sections—EIP‑1 explicitly lists them, and reviewers expect them. ([Ethereum Improvement Proposals]8)

B5. Remove “(normative)” clutter.
Make entire sections normative by default; label diverging parts “Informative” in headings.


C) Oversights & attack surfaces (things you haven’t considered enough)

  1. Comparator set pinning & drift (already called out). Without it, a later update to vault: or k8s: normalization can silently flip allow/deny; audits become non‑replayable. Pin a schemes_snapshot_id or record comparator fingerprints in PoARs. ([GitHub]4)

  2. Channel binding registry & floor semantics. Name the exact RFC bindings and ensure your channel_lattice_id snapshot enumerates them with partial order comparisons. Example elements: mtls:v1, tls-exporter:v1, dpop:v1, bearer:v1. Use RFC 9266 for TLS 1.3 exporter binding and RFC 9449 for DPoP. ([RFC Editor]5)

  3. Replay & binding collisions. Spell out nonces (jti) and one‑time use or maxUses in builtins (tightening rule must be monotone). Clarify how channel profile strings map to RFCs (above) to prevent weak aliasing.

  4. Resource explosion / selector bounds. You forbid unbounded globs/regex but permit “bounded prefixes.” Add explicit size/complexity limits on declaration sets and selector forms (max entries, max total bytes) and require CEPs to enforce resource budgets (you partly do; make it crisp).

  5. Time discipline and skew. You defer to TAP; add a one‑liner: CEP MUST capture a single now and apply TAP’s skew policy consistently to all checks (within_time, envelope windows, ttl_ok), then cite that in the algorithm (you already do; make it top‑level invariant).

  6. Reveal mode implications. PSP‑1 notes reveal is high risk; point to TAP language requiring strict penalties or forbidding for high‑assurance profiles and require PoAR to mark exposureMode=reveal. Your internal docs already argue this and define break‑glass receipts. Link it.

  7. Lease freshness verification inputs. Specify the environment fact (lease_status) and ban network I/O in builtins; say how a CEP checks lease freshness: only via TAP‑approved, locally available attestations. (You imply this; make it explicit.)

  8. Attestation of runtime (enforcer identity & version). Receipts and audits will need enforcer measurement (adapterRef/build attestation) recorded in PoAR for traceability (you have this in Receipt Rails; link from PSP‑1 to PSP‑2/TAP).

  9. Test vectors & conformance suite. Add a dedicated section with cross‑platform test vectors for:

    • PCF identity stability (different literal orders collapse to the same program_id)
    • Attenuation proofs (valid/invalid child examples)
    • Boundary time cases (nbf/exp edges)
    • Comparator normalization (tricky URIs, percent‑encoding)
      EIP‑style specs always ship tests. ([Ethereum Improvement Proposals]8)
  10. Reference to an external logic (Biscuit) must be precise. You map CPL/0 to Biscuit checks; offer a normative equivalence only for the checks‑only fragment and ban variables/rules. Cite the Biscuit spec page(s) rather than blog posts. ([Eclipse Biscuit]3)


D) Drop‑in redlines (tightening language; safe defaults)

Replace “Literals and Terms” in Language Model with:
Literals are invocations of pure, deterministic, bounded Builtins. CPL/0 does not have user‑defined atoms, variables, rules, recursion, or negation. Terms are ground and typed: Str, Int (arbitrary‑precision), Bool, Bytes.

Replace the attenuation bullets in “Attenuation by Derivation” with:
Program checks: The child MUST include all parent checks (by PCF identity) and MAY add additional checks.
Queries per check: For any retained parent Query, the child query’s literal multiset MUST be a superset; the child MAY drop any subset of the parent’s queries (narrowing).
Literal constants: Constants MUST only tighten according to the builtin’s tightening rule in the pinned builtins_id (e.g., narrower time window; lesser or equal TTL; higher or equal channel floor).
Declarations: Each child declaration MUST be a subset of the corresponding parent declaration under the same normalization/comparator semantics.

Replace the time semantics with a single “Time Model & Boundaries” clause:
The CEP captures a single logical now at enforcement start. Unless otherwise specified, all time windows are half‑open [start, end). Presentation lifetime is enforced as: now ∈ [iat, exp) AND any ttl_ok constraint; if both exp and ttl_ok are present, both MUST pass. Failures MUST deny. ([RFC Editor]2)

In “Evaluation Environment,” add:
The CEP MUST normalize resource using the same scheme comparator semantics used to canonicalize declarations before evaluation. Normalization failure MUST deny.

In “Semantic Pinning,” add:
schemes_snapshot_id (RECOMMENDED): a content‑addressed manifest pinning the exact comparator entry for each scheme name referenced by declarations. If absent, CEPs MUST record comparator fingerprints in receipts (PSP‑2). Unknown or mismatched comparator versions across delegation hops MUST deny. ([GitHub]4)

In “Builtins and registries,” add explicit profile examples and citations:
channel_geq(channel, floor) compares profiles (e.g., mtls:v1, tls-exporter:v1, dpop:v1, bearer:v1) under the pinned lattice. tls-exporter:v1 corresponds to the TLS 1.3 exporter channel binding (RFC 9266); dpop:v1 corresponds to RFC 9449 DPoP; bearer:v1 represents no sender‑constraining. ([RFC Editor]5)

In Appendix A – CDDL grammar:
Remove Sym/Var, enforce Literal = { op: tstr, args: [* Term] } with Term = Str / Int / Bool / Bytes only. Make clear: structure only; on‑wire encodings are in PSP‑3.

In “Frame and Canonical Bytes”:
Keep only the abstract identity rule in PSP‑1. Move CBOR/COSE (RFC 8949/9052), JSON/JCS (RFC 8785), DSSE envelope decisions to PSP‑3. If you want JOSE compatibility, cite JWS 7515 and JWT 7519 as profiles (optional). ([IETF Datatracker]6)

In “Security Considerations” add acceptance hooks instead of details:
Receipt emission (PoAR), deny reasons, exposure mode penalties, lease freshness, and adapter measurement (e.g., adapterRef) are defined in PSP‑2 and TAP. PSP‑1 mandates that the enforcer mint an Access PoAR on success or a DenyReceipt on failure (placement per CEP(R/P/S) profile). See Receipt Rails.


E) Smaller editorial tightening (high ROI)

  • Delete repeated “normative” markers. Use “Informative” only where needed.
  • Use consistent field casing in examples (channelBinding vs channel_binding). Since PSP‑1 doesn’t define on‑wire names, keep examples consistent in one style and remind readers the names are illustrative.
  • Consolidate “Non‑goals.” Move enforcement placement, transport, receipts, and governance pointers into a single “Out of Scope & References” section with precise forward references (PSP‑2/‑3, TAP).
  • Reason codes: Declare they live in PSP‑2 (Receipt rails), not here. Your internal doc already enumerates them and shows where PoAR is minted.

F) Things to add (short subsections)

  1. Threat model pointers. A 1‑page “threats we explicitly handle” (replay, token theft, comparator drift, clock skew, authority broadening) and “out of scope” (network I/O in evaluation, ZK proofs).
  2. IANA/Registry considerations. If you ever want to register builtin op‑ids or channel profiles, reserve a section stub now.
  3. Test vectors. Provide three complete, executable examples per domain with canonical bytes, program_id, and declaration CIDs.
  4. Interoperability with Biscuit. Define the canonical transformation for the checks‑only fragment (strict mapping rules). ([Eclipse Biscuit]3)

G) Consistency with your surrounding documents

  • Receipts & acceptance — PSP‑1 should rely on PSP‑2/TAP for PoAR/VOR/Views, reason codes, and acceptance thresholds. Your Receipt Rails doc already fixes the invariant that the enforcer mints the proof and shows placement variants. Cross‑reference rather than restating fields.
  • CEP vs BA vocabulary — your TrustOS architecture docs define CEP(R/P/S), BA as a subtype, SSA terminology, and exposure modes; incorporate that vocabulary once and reuse.
  • Reveal penalties & break‑glass receipts — call them out in Security Considerations and point to TAP constraints and Receipt Rails (BreakGlassReceipt).

H) Example micro‑diffs (ready to paste)

H1. Language Model — Literals/Terms

Before:
Uses builtin literals only: Builtin(op_id, [arg...]). Terms are ground: Str | Int | Bool | Bytes. No variables. No user‑defined atoms.
After:
Literals are builtin invocations: Builtin(op_id, [arg...]). CPL/0 has no user‑defined predicates, atoms, variables, rules, or recursion. Terms are ground and typed: Str | Int | Bool | Bytes.

H2. PCF — remove Atom, keep abstract

Before:
literal kind (e.g., Builtin before Atom if Atoms are present)
After:
sort literals by (operator id, arguments under canonical term order). CPL/0 defines only builtin literals.

H3. Attenuation

Before:
A child Program MAY add Checks; it MUST NOT remove any parent Check that controls authority…
After:
A child MUST include all parent Checks (by PCF identity) and MAY add Checks. Within each retained parent Query, the child literal multiset MUST be a superset; the child MAY drop queries (narrow the OR). Literal constants MUST only tighten per builtin rules.

H4. Time model

CEPs capture a single now. All windows are [start, end). Presentation acceptance requires now ∈ [iat, exp) and all time‑related builtins (e.g., ttl_ok) to pass; if both exp and ttl_ok are present, both MUST pass.

H5. Semantic pinning

Add schemes_snapshot_id (content‑addressed manifest) to the Grant pins. In a delegation chain the child’s schemes_snapshot_id MUST equal the parent’s.


I) Quick checklist for final pass (editorial QA)

  • Remove Atom/Sym/Var everywhere; update Appendix A grammar.
  • Align time boundaries to half‑open and state precedence of exp and ttl_ok.
  • Replace “controls authority” wording with the syntactic MUSTs above.
  • Introduce schemes_snapshot_id or require comparator fingerprints in PoAR.
  • Define channel binding profiles with RFC citations (9266, 9449) and pin via channel_lattice_id.
  • Move on‑wire specifics to PSP‑3 (CBOR/COSE, JCS/JWS/DSSE).
  • Point all receipt fields and reason codes to PSP‑2 and TAP/RAM.
  • Add a Test Vectors section.
  • Normalize capitalization/field casing across examples.

Sources (selected)

  • Requirements language (BCP14): RFC 2119; RFC 8174. ([RFC Editor]1)
  • TLS exporters & channel binding: RFC 8446 (TLS 1.3); RFC 9266 (tls‑exporter for TLS 1.3); RFC 5705 (KM exporters). ([RFC Editor]9)
  • DPoP sender‑constraining: RFC 9449. ([RFC Editor]10)
  • Canonical encodings & envelopes: RFC 8949 (CBOR); RFC 9052 (COSE); RFC 8785 (JCS); RFC 7515 (JWS); DSSE spec. ([IETF Datatracker]6)
  • CIDs/multihash for content addressing: Multiformats CID spec; IPFS docs. ([GitHub]4)
  • Biscuit checks‑only mapping: Biscuit specifications & docs. ([Eclipse Biscuit]3)
  • Receipt Rails & acceptance (internal alignment): enforcer‑minted PoAR, deny receipts, TAP/RAM, CEP/BA placement vocab.

If you want, I can produce a cleaned “PSP‑1 (Editor’s Cut)” markdown that (1) applies the redlines above, (2) removes duplication and “(normative)” noise, (3) adds a one‑page time model, and (4) stubs test vectors.

@CMCDragonkai
Copy link
Member Author

I'm going to address that review later. First is to merge.

@CMCDragonkai CMCDragonkai merged commit 6920e86 into staging Oct 14, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant